using System.Runtime.Remoting.Messaging;
using System.Web;
using NHibernate;
using NHibernate.Cfg;

namespace OrderSystem.Core
{
    public sealed class NHibernateSessionManager
    {
        private const string SESSION_KEY = "CONTEXT_SESSION";
        private const string TRANSACTION_KEY = "CONTEXT_TRANSACTION";
        private static readonly ISessionFactory sessionFactory;

        #region private properties
        private static ISession ContextSession
        {
            get
            {
                if (IsInWebContext())
                {
                    return (ISession)HttpContext.Current.Items[SESSION_KEY];
                }
                return (ISession)CallContext.GetData(SESSION_KEY);
            }
            set
            {
                if (IsInWebContext())
                {
                    HttpContext.Current.Items[SESSION_KEY] = value;
                }
                else
                {
                    CallContext.SetData(SESSION_KEY, value);
                }
            }
        }
        
        private static ITransaction ContextTransaction
        {
            get
            {
                if (IsInWebContext())
                {
                    return (ITransaction)HttpContext.Current.Items[TRANSACTION_KEY];
                }

                return (ITransaction)CallContext.GetData(TRANSACTION_KEY);
            }
            set
            {
                if (IsInWebContext())
                {
                    HttpContext.Current.Items[TRANSACTION_KEY] = value;
                }
                else
                {
                    CallContext.SetData(TRANSACTION_KEY, value);
                }
            }
        }
        #endregion


        #region constructor
        static NHibernateSessionManager()
        {
            sessionFactory = new Configuration().Configure().BuildSessionFactory();
        }
        #endregion

        public static ISession GetSession()
        {
            ISession session = ContextSession;

            if (session == null)
            {
                session = sessionFactory.OpenSession();

                ContextSession = session;
            }

            return session;
        }

        public static void CloseSession()
        {
            ISession session = ContextSession;

            if (session != null && session.IsOpen)
            {
                session.Flush();
                session.Close();
            }

            ContextSession = null;
        }

        public static void BeginTransaction()
        {
            ITransaction transaction = ContextTransaction;

            if (transaction == null)
            {
                transaction = GetSession().BeginTransaction();
                ContextTransaction = transaction;
            }
        }

        public static void CommitTransaction()
        {
            ITransaction transaction = ContextTransaction;

            try
            {
                if (HasOpenTransaction())
                {
                    transaction.Commit();
                    ContextTransaction = null;
                }
            }
            catch (HibernateException)
            {
                RollbackTransaction();
                throw;
            }
        }

        public static bool HasOpenTransaction()
        {
            ITransaction transaction = ContextTransaction;

            return transaction != null && !transaction.WasCommitted && !transaction.WasRolledBack;
        }

        public static void RollbackTransaction()
        {
            ITransaction transaction = ContextTransaction;

            try
            {
                if (HasOpenTransaction())
                {
                    transaction.Rollback();
                }

                ContextTransaction = null;
            }
            finally
            {
                CloseSession();
            }
        }

        private static bool IsInWebContext()
        {
            return HttpContext.Current != null;
        }
    }
}